package com.cardcaptorsakura.security;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 人格切换冷却管理服务
 * 控制用户人格切换的频率和冷却时间
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PersonaSwitchCooldownService {

    private final RedisTemplate<String, Object> redisTemplate;

    @Value("${app.persona.switch.cooldown.enabled:true}")
    private boolean cooldownEnabled;

    @Value("${app.persona.switch.cooldown.default-minutes:5}")
    private int defaultCooldownMinutes;

    @Value("${app.persona.switch.max-switches-per-hour:10}")
    private int maxSwitchesPerHour;

    @Value("${app.persona.switch.max-switches-per-day:50}")
    private int maxSwitchesPerDay;

    @Value("${app.persona.switch.vip-cooldown-minutes:2}")
    private int vipCooldownMinutes;

    @Value("${app.persona.switch.emergency-cooldown-minutes:1}")
    private int emergencyCooldownMinutes;

    // Redis键前缀
    private static final String COOLDOWN_KEY_PREFIX = "persona:cooldown:";
    private static final String SWITCH_COUNT_HOUR_PREFIX = "persona:count:hour:";
    private static final String SWITCH_COUNT_DAY_PREFIX = "persona:count:day:";
    private static final String SWITCH_HISTORY_PREFIX = "persona:history:";
    private static final String EMERGENCY_SWITCH_PREFIX = "persona:emergency:";

    /**
     * 人格切换结果
     */
    public static class SwitchResult {
        private boolean allowed;
        private String reason;
        private long remainingCooldownSeconds;
        private int remainingSwitchesToday;
        private int remainingSwitchesThisHour;
        private LocalDateTime nextAllowedTime;
        private Map<String, Object> metadata;

        public SwitchResult() {
            this.metadata = new HashMap<>();
        }

        // Getters and Setters
        public boolean isAllowed() { return allowed; }
        public void setAllowed(boolean allowed) { this.allowed = allowed; }
        public String getReason() { return reason; }
        public void setReason(String reason) { this.reason = reason; }
        public long getRemainingCooldownSeconds() { return remainingCooldownSeconds; }
        public void setRemainingCooldownSeconds(long remainingCooldownSeconds) { this.remainingCooldownSeconds = remainingCooldownSeconds; }
        public int getRemainingSwitchesToday() { return remainingSwitchesToday; }
        public void setRemainingSwitchesToday(int remainingSwitchesToday) { this.remainingSwitchesToday = remainingSwitchesToday; }
        public int getRemainingSwitchesThisHour() { return remainingSwitchesThisHour; }
        public void setRemainingSwitchesThisHour(int remainingSwitchesThisHour) { this.remainingSwitchesThisHour = remainingSwitchesThisHour; }
        public LocalDateTime getNextAllowedTime() { return nextAllowedTime; }
        public void setNextAllowedTime(LocalDateTime nextAllowedTime) { this.nextAllowedTime = nextAllowedTime; }
        public Map<String, Object> getMetadata() { return metadata; }
        public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
    }

    /**
     * 用户类型枚举
     */
    public enum UserType {
        NORMAL,     // 普通用户
        VIP,        // VIP用户
        PREMIUM,    // 高级用户
        ADMIN       // 管理员
    }

    /**
     * 切换类型枚举
     */
    public enum SwitchType {
        NORMAL,     // 正常切换
        EMERGENCY,  // 紧急切换
        ADMIN       // 管理员切换
    }

    /**
     * 检查是否可以切换人格
     */
    public SwitchResult canSwitchPersona(String userId, String fromPersonaId, 
                                        String toPersonaId, UserType userType, 
                                        SwitchType switchType) {
        if (!cooldownEnabled) {
            SwitchResult result = new SwitchResult();
            result.setAllowed(true);
            result.setReason("COOLDOWN_DISABLED");
            return result;
        }

        try {
            SwitchResult result = new SwitchResult();
            
            // 1. 检查管理员权限
            if (userType == UserType.ADMIN || switchType == SwitchType.ADMIN) {
                result.setAllowed(true);
                result.setReason("ADMIN_PRIVILEGE");
                return result;
            }
            
            // 2. 检查紧急切换
            if (switchType == SwitchType.EMERGENCY) {
                return checkEmergencySwitch(userId, fromPersonaId, toPersonaId, userType);
            }
            
            // 3. 检查冷却时间
            SwitchResult cooldownResult = checkCooldown(userId, userType);
            if (!cooldownResult.isAllowed()) {
                return cooldownResult;
            }
            
            // 4. 检查每小时切换次数限制
            SwitchResult hourlyResult = checkHourlyLimit(userId, userType);
            if (!hourlyResult.isAllowed()) {
                return hourlyResult;
            }
            
            // 5. 检查每日切换次数限制
            SwitchResult dailyResult = checkDailyLimit(userId, userType);
            if (!dailyResult.isAllowed()) {
                return dailyResult;
            }
            
            // 6. 检查人格特定限制
            SwitchResult personaResult = checkPersonaSpecificLimits(userId, fromPersonaId, toPersonaId);
            if (!personaResult.isAllowed()) {
                return personaResult;
            }
            
            // 7. 所有检查通过
            result.setAllowed(true);
            result.setReason("ALLOWED");
            result.setRemainingSwitchesToday(getRemainingDailySwitches(userId, userType));
            result.setRemainingSwitchesThisHour(getRemainingHourlySwitches(userId, userType));
            
            return result;
        } catch (Exception e) {
            log.error("Error checking persona switch permission for user: {}", userId, e);
            SwitchResult result = new SwitchResult();
            result.setAllowed(false);
            result.setReason("SYSTEM_ERROR");
            return result;
        }
    }

    /**
     * 记录人格切换
     */
    public void recordPersonaSwitch(String userId, String fromPersonaId, String toPersonaId, 
                                   UserType userType, SwitchType switchType) {
        if (!cooldownEnabled) {
            return;
        }

        try {
            LocalDateTime now = LocalDateTime.now();
            
            // 1. 设置冷却时间
            setCooldown(userId, userType, switchType);
            
            // 2. 增加切换计数
            incrementSwitchCount(userId, now);
            
            // 3. 记录切换历史
            recordSwitchHistory(userId, fromPersonaId, toPersonaId, switchType, now);
            
            // 4. 处理紧急切换记录
            if (switchType == SwitchType.EMERGENCY) {
                recordEmergencySwitch(userId, now);
            }
            
            log.info("Recorded persona switch for user: {}, from: {} to: {}, type: {}", 
                    userId, fromPersonaId, toPersonaId, switchType);
        } catch (Exception e) {
            log.error("Error recording persona switch for user: {}", userId, e);
        }
    }

    /**
     * 检查冷却时间
     */
    private SwitchResult checkCooldown(String userId, UserType userType) {
        SwitchResult result = new SwitchResult();
        String cooldownKey = COOLDOWN_KEY_PREFIX + userId;
        
        Long cooldownExpiry = (Long) redisTemplate.opsForValue().get(cooldownKey);
        if (cooldownExpiry != null) {
            long currentTime = System.currentTimeMillis();
            if (currentTime < cooldownExpiry) {
                result.setAllowed(false);
                result.setReason("COOLDOWN_ACTIVE");
                result.setRemainingCooldownSeconds((cooldownExpiry - currentTime) / 1000);
                result.setNextAllowedTime(LocalDateTime.now().plusSeconds(result.getRemainingCooldownSeconds()));
                return result;
            }
        }
        
        result.setAllowed(true);
        return result;
    }

    /**
     * 检查每小时限制
     */
    private SwitchResult checkHourlyLimit(String userId, UserType userType) {
        SwitchResult result = new SwitchResult();
        String hourKey = SWITCH_COUNT_HOUR_PREFIX + userId + ":" + 
                        LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
        
        Integer hourlyCount = (Integer) redisTemplate.opsForValue().get(hourKey);
        if (hourlyCount == null) {
            hourlyCount = 0;
        }
        
        int maxHourlySwitches = getMaxHourlySwitches(userType);
        if (hourlyCount >= maxHourlySwitches) {
            result.setAllowed(false);
            result.setReason("HOURLY_LIMIT_EXCEEDED");
            result.setRemainingSwitchesThisHour(0);
            return result;
        }
        
        result.setAllowed(true);
        result.setRemainingSwitchesThisHour(maxHourlySwitches - hourlyCount);
        return result;
    }

    /**
     * 检查每日限制
     */
    private SwitchResult checkDailyLimit(String userId, UserType userType) {
        SwitchResult result = new SwitchResult();
        String dayKey = SWITCH_COUNT_DAY_PREFIX + userId + ":" + 
                       LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        
        Integer dailyCount = (Integer) redisTemplate.opsForValue().get(dayKey);
        if (dailyCount == null) {
            dailyCount = 0;
        }
        
        int maxDailySwitches = getMaxDailySwitches(userType);
        if (dailyCount >= maxDailySwitches) {
            result.setAllowed(false);
            result.setReason("DAILY_LIMIT_EXCEEDED");
            result.setRemainingSwitchesToday(0);
            return result;
        }
        
        result.setAllowed(true);
        result.setRemainingSwitchesToday(maxDailySwitches - dailyCount);
        return result;
    }

    /**
     * 检查人格特定限制
     */
    private SwitchResult checkPersonaSpecificLimits(String userId, String fromPersonaId, String toPersonaId) {
        SwitchResult result = new SwitchResult();
        
        // 检查是否是相同人格
        if (fromPersonaId != null && fromPersonaId.equals(toPersonaId)) {
            result.setAllowed(false);
            result.setReason("SAME_PERSONA");
            return result;
        }
        
        // 检查人格组合限制（例如：医生人格不能直接切换到娱乐人格）
        if (isRestrictedPersonaSwitch(fromPersonaId, toPersonaId)) {
            result.setAllowed(false);
            result.setReason("RESTRICTED_PERSONA_COMBINATION");
            return result;
        }
        
        result.setAllowed(true);
        return result;
    }

    /**
     * 检查紧急切换
     */
    private SwitchResult checkEmergencySwitch(String userId, String fromPersonaId, 
                                            String toPersonaId, UserType userType) {
        SwitchResult result = new SwitchResult();
        
        // 检查紧急切换次数限制（每天最多3次）
        String emergencyKey = EMERGENCY_SWITCH_PREFIX + userId + ":" + 
                             LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        
        Integer emergencyCount = (Integer) redisTemplate.opsForValue().get(emergencyKey);
        if (emergencyCount == null) {
            emergencyCount = 0;
        }
        
        int maxEmergencySwitches = getMaxEmergencySwitches(userType);
        if (emergencyCount >= maxEmergencySwitches) {
            result.setAllowed(false);
            result.setReason("EMERGENCY_LIMIT_EXCEEDED");
            return result;
        }
        
        result.setAllowed(true);
        result.setReason("EMERGENCY_ALLOWED");
        return result;
    }

    /**
     * 设置冷却时间
     */
    private void setCooldown(String userId, UserType userType, SwitchType switchType) {
        String cooldownKey = COOLDOWN_KEY_PREFIX + userId;
        int cooldownMinutes = getCooldownMinutes(userType, switchType);
        long cooldownExpiry = System.currentTimeMillis() + (cooldownMinutes * 60 * 1000L);
        
        redisTemplate.opsForValue().set(cooldownKey, cooldownExpiry, 
                                      Duration.ofMinutes(cooldownMinutes));
    }

    /**
     * 增加切换计数
     */
    private void incrementSwitchCount(String userId, LocalDateTime now) {
        // 每小时计数
        String hourKey = SWITCH_COUNT_HOUR_PREFIX + userId + ":" + 
                        now.format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
        redisTemplate.opsForValue().increment(hourKey);
        redisTemplate.expire(hourKey, Duration.ofHours(1));
        
        // 每日计数
        String dayKey = SWITCH_COUNT_DAY_PREFIX + userId + ":" + 
                       now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        redisTemplate.opsForValue().increment(dayKey);
        redisTemplate.expire(dayKey, Duration.ofDays(1));
    }

    /**
     * 记录切换历史
     */
    private void recordSwitchHistory(String userId, String fromPersonaId, String toPersonaId, 
                                   SwitchType switchType, LocalDateTime now) {
        String historyKey = SWITCH_HISTORY_PREFIX + userId;
        
        Map<String, Object> switchRecord = new HashMap<>();
        switchRecord.put("fromPersonaId", fromPersonaId);
        switchRecord.put("toPersonaId", toPersonaId);
        switchRecord.put("switchType", switchType.name());
        switchRecord.put("timestamp", now.toString());
        
        redisTemplate.opsForList().leftPush(historyKey, switchRecord);
        redisTemplate.opsForList().trim(historyKey, 0, 99); // 保留最近100条记录
        redisTemplate.expire(historyKey, Duration.ofDays(30));
    }

    /**
     * 记录紧急切换
     */
    private void recordEmergencySwitch(String userId, LocalDateTime now) {
        String emergencyKey = EMERGENCY_SWITCH_PREFIX + userId + ":" + 
                             now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        redisTemplate.opsForValue().increment(emergencyKey);
        redisTemplate.expire(emergencyKey, Duration.ofDays(1));
    }

    /**
     * 获取冷却时间（分钟）
     */
    private int getCooldownMinutes(UserType userType, SwitchType switchType) {
        if (switchType == SwitchType.EMERGENCY) {
            return emergencyCooldownMinutes;
        }
        
        switch (userType) {
            case VIP:
            case PREMIUM:
                return vipCooldownMinutes;
            case ADMIN:
                return 0;
            default:
                return defaultCooldownMinutes;
        }
    }

    /**
     * 获取最大每小时切换次数
     */
    private int getMaxHourlySwitches(UserType userType) {
        switch (userType) {
            case VIP:
                return maxSwitchesPerHour * 2;
            case PREMIUM:
                return maxSwitchesPerHour * 3;
            case ADMIN:
                return Integer.MAX_VALUE;
            default:
                return maxSwitchesPerHour;
        }
    }

    /**
     * 获取最大每日切换次数
     */
    private int getMaxDailySwitches(UserType userType) {
        switch (userType) {
            case VIP:
                return maxSwitchesPerDay * 2;
            case PREMIUM:
                return maxSwitchesPerDay * 3;
            case ADMIN:
                return Integer.MAX_VALUE;
            default:
                return maxSwitchesPerDay;
        }
    }

    /**
     * 获取最大紧急切换次数
     */
    private int getMaxEmergencySwitches(UserType userType) {
        switch (userType) {
            case VIP:
                return 5;
            case PREMIUM:
                return 8;
            case ADMIN:
                return Integer.MAX_VALUE;
            default:
                return 3;
        }
    }

    /**
     * 获取剩余每日切换次数
     */
    private int getRemainingDailySwitches(String userId, UserType userType) {
        String dayKey = SWITCH_COUNT_DAY_PREFIX + userId + ":" + 
                       LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        
        Integer dailyCount = (Integer) redisTemplate.opsForValue().get(dayKey);
        if (dailyCount == null) {
            dailyCount = 0;
        }
        
        return Math.max(0, getMaxDailySwitches(userType) - dailyCount);
    }

    /**
     * 获取剩余每小时切换次数
     */
    private int getRemainingHourlySwitches(String userId, UserType userType) {
        String hourKey = SWITCH_COUNT_HOUR_PREFIX + userId + ":" + 
                        LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
        
        Integer hourlyCount = (Integer) redisTemplate.opsForValue().get(hourKey);
        if (hourlyCount == null) {
            hourlyCount = 0;
        }
        
        return Math.max(0, getMaxHourlySwitches(userType) - hourlyCount);
    }

    /**
     * 检查是否是受限制的人格切换组合
     */
    private boolean isRestrictedPersonaSwitch(String fromPersonaId, String toPersonaId) {
        // 定义受限制的人格切换组合
        Map<String, Set<String>> restrictedSwitches = new HashMap<>();
        restrictedSwitches.put("doctor", Set.of("entertainer", "comedian"));
        restrictedSwitches.put("therapist", Set.of("critic", "debater"));
        restrictedSwitches.put("child", Set.of("adult_content", "professional"));
        
        Set<String> restricted = restrictedSwitches.get(fromPersonaId);
        return restricted != null && restricted.contains(toPersonaId);
    }

    /**
     * 获取用户切换历史
     */
    public List<Map<String, Object>> getSwitchHistory(String userId, int limit) {
        String historyKey = SWITCH_HISTORY_PREFIX + userId;
        List<Object> history = redisTemplate.opsForList().range(historyKey, 0, limit - 1);
        
        List<Map<String, Object>> result = new ArrayList<>();
        if (history != null) {
            for (Object record : history) {
                if (record instanceof Map) {
                    result.add((Map<String, Object>) record);
                }
            }
        }
        
        return result;
    }

    /**
     * 获取用户切换统计
     */
    public Map<String, Object> getSwitchStatistics(String userId, UserType userType) {
        Map<String, Object> stats = new HashMap<>();
        
        // 今日切换次数
        String dayKey = SWITCH_COUNT_DAY_PREFIX + userId + ":" + 
                       LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        Integer todayCount = (Integer) redisTemplate.opsForValue().get(dayKey);
        stats.put("todaySwitches", todayCount != null ? todayCount : 0);
        stats.put("remainingTodaySwitches", getRemainingDailySwitches(userId, userType));
        
        // 本小时切换次数
        String hourKey = SWITCH_COUNT_HOUR_PREFIX + userId + ":" + 
                        LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
        Integer hourCount = (Integer) redisTemplate.opsForValue().get(hourKey);
        stats.put("thisHourSwitches", hourCount != null ? hourCount : 0);
        stats.put("remainingThisHourSwitches", getRemainingHourlySwitches(userId, userType));
        
        // 紧急切换次数
        String emergencyKey = EMERGENCY_SWITCH_PREFIX + userId + ":" + 
                             LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        Integer emergencyCount = (Integer) redisTemplate.opsForValue().get(emergencyKey);
        stats.put("todayEmergencySwitches", emergencyCount != null ? emergencyCount : 0);
        stats.put("remainingEmergencySwitches", 
                 Math.max(0, getMaxEmergencySwitches(userType) - (emergencyCount != null ? emergencyCount : 0)));
        
        // 冷却状态
        String cooldownKey = COOLDOWN_KEY_PREFIX + userId;
        Long cooldownExpiry = (Long) redisTemplate.opsForValue().get(cooldownKey);
        if (cooldownExpiry != null && System.currentTimeMillis() < cooldownExpiry) {
            stats.put("inCooldown", true);
            stats.put("cooldownRemainingSeconds", (cooldownExpiry - System.currentTimeMillis()) / 1000);
        } else {
            stats.put("inCooldown", false);
            stats.put("cooldownRemainingSeconds", 0);
        }
        
        return stats;
    }

    /**
     * 清除用户冷却（管理员功能）
     */
    public void clearUserCooldown(String userId) {
        String cooldownKey = COOLDOWN_KEY_PREFIX + userId;
        redisTemplate.delete(cooldownKey);
        log.info("Cleared cooldown for user: {}", userId);
    }

    /**
     * 重置用户切换计数（管理员功能）
     */
    public void resetUserSwitchCount(String userId) {
        String dayKey = SWITCH_COUNT_DAY_PREFIX + userId + ":" + 
                       LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String hourKey = SWITCH_COUNT_HOUR_PREFIX + userId + ":" + 
                        LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
        String emergencyKey = EMERGENCY_SWITCH_PREFIX + userId + ":" + 
                             LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        
        redisTemplate.delete(dayKey);
        redisTemplate.delete(hourKey);
        redisTemplate.delete(emergencyKey);
        
        log.info("Reset switch count for user: {}", userId);
    }
}